Render this report with
~/spinal_cord_paper/scripts/Gg_devel_scWGCNA_module_analysis_render.sh.
library(Seurat)
Attaching SeuratObject
library(WGCNA)
Loading required package: dynamicTreeCut
Loading required package: fastcluster
Attaching package: ‘fastcluster’
The following object is masked from ‘package:stats’:
hclust
Attaching package: ‘WGCNA’
The following object is masked from ‘package:stats’:
cor
library(tidyr)
library(ggplot2)
library(stringr)
library(patchwork)
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
── Attaching packages ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✔ tibble 3.1.8 ✔ dplyr 1.0.10
✔ readr 1.4.0 ✔ forcats 0.5.1
✔ purrr 0.3.4
── Conflicts ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
library(cowplot)
Attaching package: ‘cowplot’
The following object is masked from ‘package:patchwork’:
align_plots
library(pheatmap)
library(gridExtra)
Attaching package: ‘gridExtra’
The following object is masked from ‘package:dplyr’:
combine
Load individual seurat and test WGCNA data
The individual data sets are the Day 5 (Gg_D05_ctrl_seurat_070323),
Day 7 (Gg_D07_ctrl_seurat_070323), and Day 10 (Gg_ctrl_1_seurat_070323)
chicken spinal cord sets. The test WGCNA data are the modules calculated
on the integrated data set of all three stages.
se_path <- c("Gg_D05_ctrl_seurat_070323",
"Gg_D07_ctrl_seurat_070323",
"Gg_ctrl_1_seurat_070323")
clust_col <- read.csv("~/spinal_cord_paper/annotations/broad_cluster_marker_colors.csv") %>%
rename(broad = broad_cluster) %>%
select(-marker)
Order of the broad clusters for plotting purposes.
broad_order <- c("progenitors",
"FP",
"RP",
"FP/RP",
"neurons",
"OPC",
"MFOL",
"pericytes",
"microglia",
"blood",
"vasculature"
)
my.ses <- list()
col_table <- list()
ord_levels <- list()
for (i in seq(se_path)) {
# load the data sets
my.se <- readRDS(paste0("~/spinal_cord_paper/data/", se_path[i], ".rds"))
annot <- read.csv(list.files("~/spinal_cord_paper/annotations",
pattern = str_remove(se_path[i], "_seurat_\\d{6}"),
full.names = TRUE))
if(length(table(annot$number)) != length(table(my.se$seurat_clusters))) {
stop("Number of clusters must be identical!")
}
# rename for left join
annot <- annot %>%
mutate(fine = paste(fine, number, sep = "_")) %>%
mutate(number = factor(number, levels = 1:nrow(annot))) %>%
rename(seurat_clusters = number)
# cluster order for vln plots
ord_levels[[i]] <- annot$fine[order(match(annot$broad, broad_order))]
# create index for color coding
col_table[[i]] <- annot %>%
left_join(clust_col, by = "broad") %>%
select(c("fine", "color"))
# add cluster annotation to meta data
my.se@meta.data <- my.se@meta.data %>%
rownames_to_column("rowname") %>%
left_join(annot, by = "seurat_clusters") %>%
mutate(fine = factor(fine, levels = annot$fine)) %>%
column_to_rownames("rowname")
my.ses[[i]] <- my.se
}
names(my.ses) <- c("D05", "D07", "D10")
names(col_table) <- c("D05", "D07", "D10")
names(ord_levels) <- c("D05", "D07", "D10")
rm(my.se, annot)
# The reference WGCNA data. We can have several, if we want to test many at the same time
WGCNA_data = list()
WGCNA_data[[1]] = readRDS("~/spinal_cord_paper/output/Gg_devel_int_scWGCNA_250723.rds")
my.wsub =list()
my.wsub[[1]]= c(1:22)
# the name of each sample, as they appear in my.files and in the metadata of the combined object
my.samplenames = c("D05", "D07", "D10")
# This is just to add a little bit more sense to the modules, so that we don't get just a color. Corresponds to WGCNA_data
my.modulenames = list()
my.modulenames[[1]] = c(1:22)
Module gene correlation
Here, we do a correlation matrix / heatmap, to see which cell
clusters group togheter. This helps us to make more detailed
dotplots.
This part of the script can still be used to compare several WGCNA
datasets in parallel.
# broad cluster color table
all_col <- do.call(rbind, col_table) %>%
rownames_to_column("sample") %>%
mutate(sample = substr(sample, 1, 3)) %>%
mutate(sample_celltype = paste(sample, fine, sep = "_")) %>%
select(c("color", "sample_celltype", "sample"))
Average expression data
cell_table <- my.metam %>%
rownames_to_column("cell_ID") %>%
select("sample_celltype", "cell_ID")
avg.mod.eigengenes <- WGCNA_data[[1]]$sc.MEList$averageExpr %>%
rownames_to_column("cell_ID") %>%
left_join(cell_table, by = "cell_ID") %>%
column_to_rownames("cell_ID")
# add metadata
avg.mod.eigengenes.mean <- avg.mod.eigengenes %>%
group_by(sample_celltype) %>%
summarise_all("mean") %>%
column_to_rownames("sample_celltype")
spearman correlation heatmap
annotations
# names and colors for the heatmap annotation
annot_name <- data.frame(
"Celltypes" = all_col$sample_celltype,
"Sample" = all_col$sample,
row.names = all_col$sample_celltype)
annot_module <- data.frame(
"Module" = colnames(avg.mod.eigengenes)[1:22],
row.names = colnames(avg.mod.eigengenes)[1:22]
)
pheat_col_table <- do.call(rbind, col_table) %>%
rownames_to_column("sample") %>%
mutate(sample = substr(sample, 1,3)) %>%
mutate(fine = paste(sample, fine, sep = "_"))
# match color table with annotation
pheat_col_table <- pheat_col_table[match(annot_name$Celltypes, pheat_col_table$fine),]
annot_col <- list(
Celltypes = pheat_col_table$color,
Sample = c(D05 = "#A4A4A4",
D07 = "#515151",
D10 = "#000000"),
Module = str_remove(colnames(avg.mod.eigengenes)[1:22], "AE")
)
names(annot_col[[1]]) <- annot_name$Celltypes
names(annot_col[[3]]) <- colnames(avg.mod.eigengenes)[1:22]
heatmap of module pseudobulk average expression
pdf("~/spinal_cord_paper/figures/Fig_1_devel_module_v_clusters_heatmap.pdf", width = 8, height = 10)
grid.arrange(htmp$gtable)
dev.off()
null device
1
Load the integrated data set
The integrated data set on which the WGCNA is calculated. We plot it,
split by the original cell types from the three original samples.
# This is a file of all the combined mouse datasets, normalized and such.
my.sec = readRDS("~/spinal_cord_paper/data/Gg_devel_int_seurat_250723.rds")
identical(rownames(my.metam), colnames(my.sec))
[1] TRUE
my.metam$sample_celltype <- factor(my.metam$sample_celltype, levels = all_col$sample_celltype)
#Set the identities of the integrated data, to the annotated clusters
my.sec = SetIdent(my.sec, value = my.metam$sample_celltype)
p1 <- DimPlot(
my.sec,
reduction = "tsne",
label = TRUE,
repel = TRUE,
cols = all_col$color,
split.by = "orig.ident"
) +
NoLegend()
p1
pdf("~/spinal_cord_paper/figures/Devel_split_tsne.pdf", height = 7, width = 15)
#Plot split tsne
p1

Avg. module exp. by stage
tSNE DimPlots showing the average expression of each module by
stage.

AE over time
We plot the average expression of each module in the three stages and
the 5 broad cell type clusters present in all 3 stages.
VlnPlots of avg. module exp. by stage and seurat cluster
colored by module
# reorder seurat clusters
for (i in seq(my.ses)) {
my.ses[[i]]$seurat_clusters <- factor(
my.ses[[i]]$seurat_clusters,
levels = levels(my.ses[[i]]$seurat_clusters)[as.integer(str_extract(ord_levels[[i]], "\\d{1,2}$"))]
)
}
vplots <- list()
for (i in seq(my.ses)) {
mods <- colnames(my.ses[[i]][[]])[grep("^AE",colnames(my.ses[[i]][[]]))]
vplots[[i]] <- VlnPlot(
my.ses[[i]],
features = mods[module_order],
group.by = "seurat_clusters",
stack = TRUE, flip = TRUE,
cols = substring(mods, 3)[module_order]) +
theme(legend.position = "none") +
geom_hline(yintercept = 0, lty = "dashed")
}
vplots[[1]]
vplots[[2]]
vplots[[3]]
pdf("~/spinal_cord_paper/figures/Fig_2_AE_by_cluster_modcol.pdf", height = 20, width = 10)
vplots[[1]]
vplots[[2]]
vplots[[3]]
colored by cell type
clust_col <- read.csv("~/spinal_cord_paper/annotations/broad_cluster_marker_colors.csv")
vplots_id <- list()
for (i in seq(my.ses)) {
mods <- colnames(my.ses[[i]][[]])[grep("^AE",colnames(my.ses[[i]][[]]))]
vplots_id[[i]] <- VlnPlot(
my.ses[[i]],
features = mods[module_order],
group.by = "seurat_clusters",
stack = TRUE, flip = TRUE,
fill.by = "ident",
cols = col_table[[i]]$color[as.integer(str_extract(ord_levels[[i]], "\\d{1,2}$"))]) +
theme(legend.position = "none") +
geom_hline(yintercept = 0, lty = "dashed")
}
vplots_id[[1]]
vplots_id[[2]]
vplots_id[[3]]
pdf("~/spinal_cord_paper/figures/Fig_2_AE_by_cluster_clucol.pdf", height = 20, width = 10)
vplots_id[[1]]
vplots_id[[2]]
vplots_id[[3]]
MN modules
For the figures we specifically select the two MN modules and plot
them as Vln plots.

VlnPlot(
my.sec,
features = mods[module_order],
group.by = "sample_celltype",
stack = TRUE, flip = TRUE,
cols = substring(mods, 3)[module_order]) +
theme(legend.position = "none") +
geom_hline(yintercept = 0, lty = "dashed")
pdf("~/spinal_cord_paper/figures/Fig_2_AE_by_cluster_integrated_data.pdf", height = 20, width = 30)
VlnPlot(
my.sec,
features = mods[module_order],
group.by = "sample_celltype",
stack = TRUE, flip = TRUE,
cols = substring(mods, 3)[module_order]) +
theme(legend.position = "none") +
geom_hline(yintercept = 0, lty = "dashed")
```r
# Date and time of Rendering
Sys.time()
sessionInfo()
```
LS0tCnRpdGxlOiAiRGV2ZWxfaW50IFdHQ05BIG1vZHVsZXMgZXhwcmVzc2lvbiBpbiBOVCBENSwgRDcsIGFuZCBEMTAgc2FtcGxlcyIKYXV0aG9yOiAiRmFiaW8gU2FjaGVyIgpkYXRlOiAiMDQuMDguMjAyMyIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IFRSVUUKICAgIHRvY19mbG9hdDogVFJVRQogIGh0bWxfbm90ZWJvb2s6CiAgICBmaWdfaGVpZ2h0OiA3CiAgICBmaWdfd2lkdGg6IDgKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KClJlbmRlciB0aGlzIHJlcG9ydCB3aXRoIH4vc3BpbmFsX2NvcmRfcGFwZXIvc2NyaXB0cy9HZ19kZXZlbF9zY1dHQ05BX21vZHVsZV9hbmFseXNpc19yZW5kZXIuc2guCgpgYGB7ciBzZXR1cH0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoV0dDTkEpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeShncmlkRXh0cmEpCmBgYAoKIyBMb2FkIGluZGl2aWR1YWwgc2V1cmF0IGFuZCB0ZXN0IFdHQ05BIGRhdGEKClRoZSBpbmRpdmlkdWFsIGRhdGEgc2V0cyBhcmUgdGhlIERheSA1IChHZ19EMDVfY3RybF9zZXVyYXRfMDcwMzIzKSwgRGF5IDcgKEdnX0QwN19jdHJsX3NldXJhdF8wNzAzMjMpLCBhbmQgRGF5IDEwIChHZ19jdHJsXzFfc2V1cmF0XzA3MDMyMykgY2hpY2tlbiBzcGluYWwgY29yZCBzZXRzLiBUaGUgdGVzdCBXR0NOQSBkYXRhIGFyZSB0aGUgbW9kdWxlcyBjYWxjdWxhdGVkIG9uIHRoZSBpbnRlZ3JhdGVkIGRhdGEgc2V0IG9mIGFsbCB0aHJlZSBzdGFnZXMuCgpgYGB7ciBkYXRhLXNldHN9CnNlX3BhdGggPC0gYygiR2dfRDA1X2N0cmxfc2V1cmF0XzA3MDMyMyIsCiAgICAgICAgICAgICAiR2dfRDA3X2N0cmxfc2V1cmF0XzA3MDMyMyIsCiAgICAgICAgICAgICAiR2dfY3RybF8xX3NldXJhdF8wNzAzMjMiKQoKY2x1c3RfY29sIDwtIHJlYWQuY3N2KCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2Fubm90YXRpb25zL2Jyb2FkX2NsdXN0ZXJfbWFya2VyX2NvbG9ycy5jc3YiKSAlPiUgCiAgcmVuYW1lKGJyb2FkID0gYnJvYWRfY2x1c3RlcikgJT4lIAogIHNlbGVjdCgtbWFya2VyKQoKYGBgCgpPcmRlciBvZiB0aGUgYnJvYWQgY2x1c3RlcnMgZm9yIHBsb3R0aW5nIHB1cnBvc2VzLgoKYGBge3Igb3JkZXJpbmd9CmJyb2FkX29yZGVyIDwtIGMoInByb2dlbml0b3JzIiwKICAgICAgIkZQIiwKICAgICAgIlJQIiwKICAgICAgIkZQL1JQIiwKICAgICAgIm5ldXJvbnMiLAogICAgICAiT1BDIiwKICAgICAgIk1GT0wiLAogICAgICAicGVyaWN5dGVzIiwKICAgICAgIm1pY3JvZ2xpYSIsCiAgICAgICJibG9vZCIsCiAgICAgICJ2YXNjdWxhdHVyZSIKICAgICAgKQpgYGAKCgpgYGB7ciBzZXVyYXQtb2JqZWN0cy1hbmQtYW5ub3RhdGlvbnN9Cm15LnNlcyA8LSBsaXN0KCkKY29sX3RhYmxlIDwtIGxpc3QoKQpvcmRfbGV2ZWxzIDwtIGxpc3QoKQoKZm9yIChpIGluIHNlcShzZV9wYXRoKSkgewogICMgbG9hZCB0aGUgZGF0YSBzZXRzCiAgbXkuc2UgPC0gcmVhZFJEUyhwYXN0ZTAoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZGF0YS8iLCBzZV9wYXRoW2ldLCAiLnJkcyIpKQogIGFubm90IDwtIHJlYWQuY3N2KGxpc3QuZmlsZXMoIn4vc3BpbmFsX2NvcmRfcGFwZXIvYW5ub3RhdGlvbnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9IHN0cl9yZW1vdmUoc2VfcGF0aFtpXSwgIl9zZXVyYXRfXFxkezZ9IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVFJVRSkpCiAgCiAgaWYobGVuZ3RoKHRhYmxlKGFubm90JG51bWJlcikpICE9IGxlbmd0aCh0YWJsZShteS5zZSRzZXVyYXRfY2x1c3RlcnMpKSkgewogICAgIHN0b3AoIk51bWJlciBvZiBjbHVzdGVycyBtdXN0IGJlIGlkZW50aWNhbCEiKQogIH0KICAKICAjIHJlbmFtZSBmb3IgbGVmdCBqb2luCiAgYW5ub3QgPC0gYW5ub3QgJT4lIAogICAgbXV0YXRlKGZpbmUgPSBwYXN0ZShmaW5lLCBudW1iZXIsIHNlcCA9ICJfIikpICU+JSAKICAgIG11dGF0ZShudW1iZXIgPSBmYWN0b3IobnVtYmVyLCBsZXZlbHMgPSAxOm5yb3coYW5ub3QpKSkgJT4lIAogICAgcmVuYW1lKHNldXJhdF9jbHVzdGVycyA9IG51bWJlcikgCiAgCiAgIyBjbHVzdGVyIG9yZGVyIGZvciB2bG4gcGxvdHMKICBvcmRfbGV2ZWxzW1tpXV0gPC0gYW5ub3QkZmluZVtvcmRlcihtYXRjaChhbm5vdCRicm9hZCwgYnJvYWRfb3JkZXIpKV0KICAKICAjIGNyZWF0ZSBpbmRleCBmb3IgY29sb3IgY29kaW5nCiAgY29sX3RhYmxlW1tpXV0gPC0gYW5ub3QgJT4lCiAgICBsZWZ0X2pvaW4oY2x1c3RfY29sLCBieSA9ICJicm9hZCIpICU+JSAKICAgIHNlbGVjdChjKCJmaW5lIiwgImNvbG9yIikpCiAgCiAgIyBhZGQgY2x1c3RlciBhbm5vdGF0aW9uIHRvIG1ldGEgZGF0YQogIG15LnNlQG1ldGEuZGF0YSA8LSBteS5zZUBtZXRhLmRhdGEgJT4lIAogICAgcm93bmFtZXNfdG9fY29sdW1uKCJyb3duYW1lIikgJT4lIAogICAgbGVmdF9qb2luKGFubm90LCBieSA9ICJzZXVyYXRfY2x1c3RlcnMiKSAlPiUgCiAgICBtdXRhdGUoZmluZSA9IGZhY3RvcihmaW5lLCBsZXZlbHMgPSBhbm5vdCRmaW5lKSkgJT4lIAogICAgY29sdW1uX3RvX3Jvd25hbWVzKCJyb3duYW1lIikKICAKICBteS5zZXNbW2ldXSA8LSBteS5zZQoKfQoKbmFtZXMobXkuc2VzKSA8LSBjKCJEMDUiLCAiRDA3IiwgIkQxMCIpCm5hbWVzKGNvbF90YWJsZSkgPC0gYygiRDA1IiwgIkQwNyIsICJEMTAiKQpuYW1lcyhvcmRfbGV2ZWxzKSA8LSBjKCJEMDUiLCAiRDA3IiwgIkQxMCIpCgpybShteS5zZSwgYW5ub3QpCmBgYAoKYGBge3IgaW5wdXQgZGF0YSwgZWNobz1UUlVFfQojIFRoZSByZWZlcmVuY2UgV0dDTkEgZGF0YS4gV2UgY2FuIGhhdmUgc2V2ZXJhbCwgaWYgd2Ugd2FudCB0byB0ZXN0IG1hbnkgYXQgdGhlIHNhbWUgdGltZQpXR0NOQV9kYXRhID0gbGlzdCgpCldHQ05BX2RhdGFbWzFdXSA9IHJlYWRSRFMoIn4vc3BpbmFsX2NvcmRfcGFwZXIvb3V0cHV0L0dnX2RldmVsX2ludF9zY1dHQ05BXzI1MDcyMy5yZHMiKQpteS53c3ViID1saXN0KCkKbXkud3N1YltbMV1dPSBjKDE6MjIpCgojIHRoZSBuYW1lIG9mIGVhY2ggc2FtcGxlLCBhcyB0aGV5IGFwcGVhciBpbiBteS5maWxlcyBhbmQgaW4gdGhlIG1ldGFkYXRhIG9mIHRoZSBjb21iaW5lZCBvYmplY3QKbXkuc2FtcGxlbmFtZXMgPSBjKCJEMDUiLCAiRDA3IiwgIkQxMCIpCgojIFRoaXMgaXMganVzdCB0byBhZGQgYSBsaXR0bGUgYml0IG1vcmUgc2Vuc2UgdG8gdGhlIG1vZHVsZXMsIHNvIHRoYXQgd2UgZG9uJ3QgZ2V0IGp1c3QgYSBjb2xvci4gQ29ycmVzcG9uZHMgdG8gV0dDTkFfZGF0YQpteS5tb2R1bGVuYW1lcyA9IGxpc3QoKQpteS5tb2R1bGVuYW1lc1tbMV1dID0gYygxOjIyKQoKYGBgCgojIE1vZHVsZSBnZW5lIGNvcnJlbGF0aW9uCgpIZXJlLCB3ZSBkbyBhIGNvcnJlbGF0aW9uIG1hdHJpeCAvIGhlYXRtYXAsIHRvIHNlZSB3aGljaCBjZWxsIGNsdXN0ZXJzIGdyb3VwIHRvZ2hldGVyLiBUaGlzIGhlbHBzIHVzIHRvIG1ha2UgbW9yZSBkZXRhaWxlZCBkb3RwbG90cy4gIApUaGlzIHBhcnQgb2YgdGhlIHNjcmlwdCBjYW4gc3RpbGwgYmUgdXNlZCB0byBjb21wYXJlIHNldmVyYWwgV0dDTkEgZGF0YXNldHMgaW4gcGFyYWxsZWwuICAKCmBgYHtyfQojIGJyb2FkIGNsdXN0ZXIgY29sb3IgdGFibGUKYWxsX2NvbCA8LSBkby5jYWxsKHJiaW5kLCBjb2xfdGFibGUpICU+JSAKICByb3duYW1lc190b19jb2x1bW4oInNhbXBsZSIpICU+JSAKICBtdXRhdGUoc2FtcGxlID0gc3Vic3RyKHNhbXBsZSwgMSwgMykpICU+JSAKICBtdXRhdGUoc2FtcGxlX2NlbGx0eXBlID0gcGFzdGUoc2FtcGxlLCBmaW5lLCBzZXAgPSAiXyIpKSAlPiUgCiAgc2VsZWN0KGMoImNvbG9yIiwgInNhbXBsZV9jZWxsdHlwZSIsICJzYW1wbGUiKSkKYGBgCgojIE1ldGEgZGF0YQoKYGBge3IgbWV0YS1kYXRhfQojR2V0IGEgZGF0YWZyYW1lIHdpdGggYW5ub3RhdGlvbnMgZm9yIGFsbCB0aGUgc2FtcGxlcyBhbmQgY29sb3JzIHdlIG5lZWQuCm15Lm1ldGFtIDwtIGxpc3QoKQoKZm9yIChpIGluIHNlcShteS5zZXMpKSB7CiAgbXkubWV0YW1bW2ldXSA8LSBteS5zZXNbW2ldXVtbXV0KICByb3duYW1lcyhteS5tZXRhbVtbaV1dKSA8LSBwYXN0ZTAocm93bmFtZXMobXkubWV0YW1bW2ldXSksICJfIiwgaSkKfQpteS5tZXRhbSA8LSBkby5jYWxsKHJiaW5kLCBteS5tZXRhbSkKCm15Lm1ldGFtJG9yaWcuaWRlbnQgPC0gc3RyX3JlcGxhY2VfYWxsKG15Lm1ldGFtJG9yaWcuaWRlbnQsIHBhdHRlcm4gPSAgIkdnX0QwNV9jdHJsIiwgIkQwNSIpCm15Lm1ldGFtJG9yaWcuaWRlbnQgPC0gc3RyX3JlcGxhY2VfYWxsKG15Lm1ldGFtJG9yaWcuaWRlbnQsIHBhdHRlcm4gPSAgIkdnX0QwN19jdHJsIiwgIkQwNyIpCm15Lm1ldGFtJG9yaWcuaWRlbnQgPC0gc3RyX3JlcGxhY2VfYWxsKG15Lm1ldGFtJG9yaWcuaWRlbnQsIHBhdHRlcm4gPSAgIkdnX2N0cmxfMSIsICJEMTAiKQoKIyBteS5tZXRhbSRzYW1wbGVfY2VsbHR5cGUgPSBwYXN0ZTAoc3Vic3RyKG15Lm1ldGFtJG9yaWcuaWRlbnQsNyw5KSwiXyIsbXkubWV0YW0kc2V1cmF0X2NsdXN0ZXJzKQpteS5tZXRhbSRzYW1wbGVfY2VsbHR5cGUgPSBwYXN0ZTAobXkubWV0YW0kb3JpZy5pZGVudCwgIl8iLCBteS5tZXRhbSRmaW5lKQoKbXkubWV0YW0gPC0gbXkubWV0YW0gJT4lIAogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJjZWxsX0lEIikgJT4lCiAgZHBseXI6OmxlZnRfam9pbihhbGxfY29sLCBieSA9ICJzYW1wbGVfY2VsbHR5cGUiKSAlPiUKICB0aWJibGU6OmNvbHVtbl90b19yb3duYW1lcyh2YXIgPSAiY2VsbF9JRCIpCgoKIyBnZXQgc2FtcGxlIGNvbG9ycwpteS5jb2xzbSA9IGMoImdyZXkiLCAiZ3JleTMwIiwgImJsYWNrIikKbmFtZXMobXkuY29sc20pIDwtIGMoIkQwNSIsICJEMDciLCAiRDEwIikKIApgYGAKCgojIEF2ZXJhZ2UgZXhwcmVzc2lvbiBkYXRhCgpgYGB7cn0KCmNlbGxfdGFibGUgPC0gbXkubWV0YW0gJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbigiY2VsbF9JRCIpICU+JSAKICBzZWxlY3QoInNhbXBsZV9jZWxsdHlwZSIsICJjZWxsX0lEIikKCmF2Zy5tb2QuZWlnZW5nZW5lcyA8LSBXR0NOQV9kYXRhW1sxXV0kc2MuTUVMaXN0JGF2ZXJhZ2VFeHByICU+JSAKICByb3duYW1lc190b19jb2x1bW4oImNlbGxfSUQiKSAlPiUgCiAgbGVmdF9qb2luKGNlbGxfdGFibGUsIGJ5ID0gImNlbGxfSUQiKSAlPiUgCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJjZWxsX0lEIikKICAKCiMgYWRkIG1ldGFkYXRhCgphdmcubW9kLmVpZ2VuZ2VuZXMubWVhbiA8LSBhdmcubW9kLmVpZ2VuZ2VuZXMgJT4lCiAgZ3JvdXBfYnkoc2FtcGxlX2NlbGx0eXBlKSAlPiUKICBzdW1tYXJpc2VfYWxsKCJtZWFuIikgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJzYW1wbGVfY2VsbHR5cGUiKQoKYGBgCgojIyBzcGVhcm1hbiBjb3JyZWxhdGlvbiBoZWF0bWFwCgojIyMgYW5ub3RhdGlvbnMKCmBgYHtyIGFubm90LWxpc3R9CgojIG5hbWVzIGFuZCBjb2xvcnMgZm9yIHRoZSBoZWF0bWFwIGFubm90YXRpb24KYW5ub3RfbmFtZSA8LSBkYXRhLmZyYW1lKAogICJDZWxsdHlwZXMiID0gYWxsX2NvbCRzYW1wbGVfY2VsbHR5cGUsCiAgIlNhbXBsZSIgICAgPSBhbGxfY29sJHNhbXBsZSwKICByb3cubmFtZXMgPSBhbGxfY29sJHNhbXBsZV9jZWxsdHlwZSkKICAKCmFubm90X21vZHVsZSA8LSBkYXRhLmZyYW1lKAogICJNb2R1bGUiID0gY29sbmFtZXMoYXZnLm1vZC5laWdlbmdlbmVzKVsxOjIyXSwKICByb3cubmFtZXMgPSBjb2xuYW1lcyhhdmcubW9kLmVpZ2VuZ2VuZXMpWzE6MjJdCikKCnBoZWF0X2NvbF90YWJsZSA8LSBkby5jYWxsKHJiaW5kLCBjb2xfdGFibGUpICU+JSAKICByb3duYW1lc190b19jb2x1bW4oInNhbXBsZSIpICU+JSAKICBtdXRhdGUoc2FtcGxlID0gc3Vic3RyKHNhbXBsZSwgMSwzKSkgJT4lIAogIG11dGF0ZShmaW5lID0gcGFzdGUoc2FtcGxlLCBmaW5lLCBzZXAgPSAiXyIpKQoKIyBtYXRjaCBjb2xvciB0YWJsZSB3aXRoIGFubm90YXRpb24KcGhlYXRfY29sX3RhYmxlIDwtIHBoZWF0X2NvbF90YWJsZVttYXRjaChhbm5vdF9uYW1lJENlbGx0eXBlcywgcGhlYXRfY29sX3RhYmxlJGZpbmUpLF0KCmFubm90X2NvbCA8LSBsaXN0KAogIENlbGx0eXBlcyA9IHBoZWF0X2NvbF90YWJsZSRjb2xvciwKICBTYW1wbGUgPSBjKEQwNSA9ICIjQTRBNEE0IiwKICAgICAgICAgICAgIEQwNyA9ICIjNTE1MTUxIiwKICAgICAgICAgICAgIEQxMCA9ICIjMDAwMDAwIiksCiAgTW9kdWxlID0gc3RyX3JlbW92ZShjb2xuYW1lcyhhdmcubW9kLmVpZ2VuZ2VuZXMpWzE6MjJdLCAiQUUiKQogICkKCm5hbWVzKGFubm90X2NvbFtbMV1dKSA8LSBhbm5vdF9uYW1lJENlbGx0eXBlcwpuYW1lcyhhbm5vdF9jb2xbWzNdXSkgPC0gY29sbmFtZXMoYXZnLm1vZC5laWdlbmdlbmVzKVsxOjIyXQoKYGBgCgoKIyMgaGVhdG1hcCBvZiBtb2R1bGUgcHNldWRvYnVsayBhdmVyYWdlIGV4cHJlc3Npb24KCmBgYHtyLCAgZmlnLmhlaWdodD0xNSwgZmlnLndpZHRoPTEwfQojIG1vZHVsZSBjb2xvcnMKbXkuY29sY29scyA9IGFzLm1hdHJpeChuYW1lcyh0YWJsZShXR0NOQV9kYXRhW1sxXV0kZHluYW1pY0NvbHMpKSkKCmh0bXAgPC0gcGhlYXRtYXAoYXMubWF0cml4KGF2Zy5tb2QuZWlnZW5nZW5lcy5tZWFuKSwKICAgICAgICAgZm9udHNpemUgPSA4LAogICAgICAgICBzY2FsZSA9ICJjb2x1bW4iLAogICAgICAgICBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygiIzRkMmQ4NyIsIiM3YzU1YTMiLCAid2hpdGUiLCAiI2VkOTkyMSIsICIjODk1ZDI1IikpKG4gPSAxMDAwKSwKICAgICAgICAgYW5ub3RhdGlvbl9yb3cgPSBhbm5vdF9uYW1lLAogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IGFubm90X21vZHVsZSwKICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vdF9jb2wsCiAgICAgICAgIGFubm90YXRpb25fbGVnZW5kID0gRiwKICAgICAgICAgYm9yZGVyX2NvbG9yID0gTkEpCgoKIyBjbHVzdGVyaW5nIGZyb20gdGhlIHNjYWxlZCBoZWF0bWFwCnJvd19vcmRlciA8LSBodG1wW1sidHJlZV9yb3ciXV0KY29sX29yZGVyIDwtIGh0bXBbWyJ0cmVlX2NvbCJdXQoKCiMgdGhyZXNob2xkIHRoZSB2YWx1ZXMgdG8gYWJzKG1hdCkgPSA1Cm1hdF9zY2FsZWQgPC0gc2NhbGUoYXMubWF0cml4KGF2Zy5tb2QuZWlnZW5nZW5lcy5tZWFuKSkKbWF0X3NjYWxlZFttYXRfc2NhbGVkID4gNV0gPC0gNQptYXRfc2NhbGVkW21hdF9zY2FsZWQgPCAtNV0gPC0gLTUKCgptYXg1IDwtIHBoZWF0bWFwKGFzLm1hdHJpeChtYXRfc2NhbGVkKSwKICAgICAgICAgICAgICAgICBmb250c2l6ZSA9IDgsCiAgICAgICAgICAgICAgICAgY29sb3IgPSBjb2xvclJhbXBQYWxldHRlKGMoIiM0ZDJkODciLCIjN2M1NWEzIiwgIndoaXRlIiwgIiNlZDk5MjEiLCAiIzg5NWQyNSIpKShuID0gMTAwMCksCiAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gcm93X29yZGVyLAogICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29scyA9IGNvbF9vcmRlciwKICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX3JvdyA9IGFubm90X25hbWUsCiAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBhbm5vdF9tb2R1bGUsCiAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vdF9jb2wsCiAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9sZWdlbmQgPSBGLAogICAgICAgICAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BKQoKIyBtb2R1bGUgb3JkZXIgdmVjdG9yIChub3QgaGNsdXN0IG9iamVjdCBhcyBhYm92ZSkKbW9kdWxlX29yZGVyIDwtIGh0bXBbWyJ0cmVlX2NvbCJdXSRvcmRlcgoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzFfZGV2ZWxfbW9kdWxlX3ZfY2x1c3RlcnNfaGVhdG1hcC5wZGYiLCB3aWR0aCA9IDgsIGhlaWdodCA9IDEwKQpncmlkLmFycmFuZ2UobWF4NSRndGFibGUpCmRldi5vZmYoKQoKYGBgCgoKCiMgTG9hZCB0aGUgaW50ZWdyYXRlZCBkYXRhIHNldAoKVGhlIGludGVncmF0ZWQgZGF0YSBzZXQgb24gd2hpY2ggdGhlIFdHQ05BIGlzIGNhbGN1bGF0ZWQuIFdlIHBsb3QgaXQsIHNwbGl0IGJ5IHRoZSBvcmlnaW5hbCBjZWxsIHR5cGVzIGZyb20gdGhlIHRocmVlIG9yaWdpbmFsIHNhbXBsZXMuIAoKYGBge3IgZGltcGxvdHMsIGZpZy53aWR0aD0xMH0KIyBUaGlzIGlzIGEgZmlsZSBvZiBhbGwgdGhlIGNvbWJpbmVkIG1vdXNlIGRhdGFzZXRzLCBub3JtYWxpemVkIGFuZCBzdWNoLgpteS5zZWMgPSByZWFkUkRTKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2RhdGEvR2dfZGV2ZWxfaW50X3NldXJhdF8yNTA3MjMucmRzIikKCmlkZW50aWNhbChyb3duYW1lcyhteS5tZXRhbSksIGNvbG5hbWVzKG15LnNlYykpCgpteS5tZXRhbSRzYW1wbGVfY2VsbHR5cGUgPC0gZmFjdG9yKG15Lm1ldGFtJHNhbXBsZV9jZWxsdHlwZSwgbGV2ZWxzID0gYWxsX2NvbCRzYW1wbGVfY2VsbHR5cGUpCiNTZXQgdGhlIGlkZW50aXRpZXMgb2YgdGhlIGludGVncmF0ZWQgZGF0YSwgdG8gdGhlIGFubm90YXRlZCBjbHVzdGVycwpteS5zZWMgPSBTZXRJZGVudChteS5zZWMsIHZhbHVlID0gbXkubWV0YW0kc2FtcGxlX2NlbGx0eXBlKQoKcDEgPC0gRGltUGxvdCgKICBteS5zZWMsCiAgcmVkdWN0aW9uID0gInRzbmUiLAogIGxhYmVsID0gVFJVRSwKICByZXBlbCA9IFRSVUUsCiAgY29scyA9IGFsbF9jb2wkY29sb3IsCiAgc3BsaXQuYnkgPSAib3JpZy5pZGVudCIKICApICsgCiAgTm9MZWdlbmQoKQoKcDEKCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0RldmVsX3NwbGl0X3RzbmUucGRmIiwgaGVpZ2h0ID0gNywgd2lkdGggPSAxNSkKI1Bsb3Qgc3BsaXQgdHNuZQpwMQpgYGAKCiMgQXZnLiBtb2R1bGUgZXhwLiBieSBzdGFnZQoKdFNORSBEaW1QbG90cyBzaG93aW5nIHRoZSBhdmVyYWdlIGV4cHJlc3Npb24gb2YgZWFjaCBtb2R1bGUgYnkgc3RhZ2UuCgpgYGB7ciBBRSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NDB9Cgpmb3IgKGkgaW4gc2VxKG15LnNlcykpIHsKICAjIHByZXBhcmUgYXZlcmFnZSBleHByZXNzaW9uIHRhYmxlCiAgdG1wIDwtIGF2Zy5tb2QuZWlnZW5nZW5lc1ssMToyMl0gJT4lCiAgICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigiY2VsbF9JRCIpICU+JQogICAgZHBseXI6OmZpbHRlcihncmVwbChwYXN0ZTAoIl8iLCBpLCAiJCIpLCBjZWxsX0lEKSkgJT4lCiAgICBkcGx5cjo6bXV0YXRlKGNlbGxfSUQgPSBzdHJpbmdyOjpzdHJfcmVtb3ZlX2FsbChjZWxsX0lELCBwYXN0ZTAoIl8iLCBpKSkpICU+JQogICAgdGliYmxlOjpjb2x1bW5fdG9fcm93bmFtZXMoImNlbGxfSUQiKQogIAogIGlkZW50aWNhbChyb3duYW1lcyh0bXApLCBjb2xuYW1lcyhteS5zZXNbW2ldXSkpCiAgIyBhZGQgbWV0YSBkYXRhIHRvIHRoZSBzZXVyYXQgb2JqZWN0cwogIG15LnNlc1tbaV1dIDwtIEFkZE1ldGFEYXRhKG15LnNlc1tbaV1dLCB0bXApCn0KCiNtYXggYW5kIG1pbiBleHByZXNzaW9uIHBlciBtb2R1bGUgKGNvbHVtbiBtYXgpCm1vZF9tYXggPC0gYXBwbHkoYXZnLm1vZC5laWdlbmdlbmVzWywxOjIyXSwgTUFSR0lOID0gMiwgRlVOID0gbWF4KVttb2R1bGVfb3JkZXJdCm1vZF9taW4gPC0gYXBwbHkoYXZnLm1vZC5laWdlbmdlbmVzWywxOjIyXSwgTUFSR0lOID0gMiwgRlVOID0gbWluKVttb2R1bGVfb3JkZXJdCgptb2RwbG90cyA8LSBsaXN0KCkKbW9kcGxvdHNbWzFdXSA8LSBsaXN0KCkKbW9kcGxvdHNbWzJdXSA8LSBsaXN0KCkKbW9kcGxvdHNbWzNdXSA8LSBsaXN0KCkKCm1vZHVsZXNfaW5fb3JkZXIgPC0gY29sbmFtZXModG1wKVttb2R1bGVfb3JkZXJdCgojIHBsb3QgdGhlIG1vZHVsZXMgc3BsaXQgdG8gdGhlIHN0YWdlcwpmb3IgKGkgaW4gc2VxKG15LnNlcykpIHsKICBmb3IgKGogaW4gc2VxKG5jb2wodG1wKSkpIHsKICAKICAgIG1vZHBsb3RzW1tpXV1bW2pdXSAgPC0gRmVhdHVyZVBsb3QoCiAgICAgIG15LnNlc1tbaV1dLCBvcmRlciA9IFRSVUUsCiAgICAgIGZlYXR1cmVzID0gbW9kdWxlc19pbl9vcmRlcltqXSwKICAgICAgcmVkdWN0aW9uID0gInRzbmUiCiAgICAgICkgKwogICAgICBnZ3RpdGxlKHN0cmluZ3I6OnN0cl9yZW1vdmUobW9kdWxlc19pbl9vcmRlcltqXSwiXkFFIikpICsKICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93PSJpdm9yeTIiLCBoaWdoPXN1YnN0cmluZyhtb2R1bGVzX2luX29yZGVyW2pdLCAzKSwgI2NvbG9ycyBpbiB0aGUgc2NhbGUKICAgICAgICAgICAgICAgICBsaW1pdHM9Yyhtb2RfbWluW2pdLCBtb2RfbWF4W2pdKSkgI3NhbWUgbGltaXRzIGZvciBwbG90cwoKICAgIAogICAgfQp9CgpmdWxsX3Bsb3QgPC0gYyhtb2RwbG90c1tbMV1dLCBtb2RwbG90c1tbMl1dLCBtb2RwbG90c1tbM11dKQoKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoZ3JvYnMgPSBmdWxsX3Bsb3QsIG5jb2wgPSAzLCBhcy50YWJsZSA9IEZBTFNFKQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzJfZGV2ZWxfbW9kdWxlc19BRV9wbG90cy5wZGYiLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA3MCkKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoZ3JvYnMgPSBmdWxsX3Bsb3QsIG5jb2wgPSAzLCBhcy50YWJsZSA9IEZBTFNFKQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvU3VwcF9GaWdfMl9tb2R1bGVzX2RhcmtncmVlbl9BRV9wbG90cy5wZGYiLCB3aWR0aCA9IDEzLCBoZWlnaHQgPSA0KQooZnVsbF9wbG90W1s5XV0gKyBmdWxsX3Bsb3RbWzMxXV0gKyBmdWxsX3Bsb3RbWzUzXV0pICsgcGxvdF9sYXlvdXQobmNvbCA9IDMsIGd1aWRlcyA9ICJjb2xsZWN0IikKYGBgCgojIEFFIG92ZXIgdGltZQoKV2UgcGxvdCB0aGUgYXZlcmFnZSBleHByZXNzaW9uIG9mIGVhY2ggbW9kdWxlIGluIHRoZSB0aHJlZSBzdGFnZXMgYW5kIHRoZSA1IGJyb2FkIGNlbGwgdHlwZSBjbHVzdGVycyBwcmVzZW50IGluIGFsbCAzIHN0YWdlcy4KCmBgYHtyIEFFLW92ZXItdGltZSwgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9MTJ9CiMgbW9kdWxlIGFubm90YXRpb25zCm1vZF9hbm5vdCA8LSByZWFkLmNzdigifi9zcGluYWxfY29yZF9wYXBlci9hbm5vdGF0aW9ucy9HZ19kZXZlbF9pbnRfc2NXR0NOQV9tb2R1bGVfYW5ub3RhdGlvbi5jc3YiKSAlPiUKICBkcGx5cjo6bXV0YXRlKG1vZHVsZSA9IHN0cl9yZXBsYWNlX2FsbChtb2R1bGUsICJcXGR7MSwyfVxcXyIsICJBRSIpKQoKbWV0YSA8LSBsaXN0KCkKCmZvciAoaSBpbiBzZXEobXkuc2VzKSkgewogIG1ldGFbW2ldXSA8LSBteS5zZXNbW2ldXUBtZXRhLmRhdGEgJT4lCiAgICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigiY2VsbF9JRCIpICU+JQogICAgZHBseXI6Om11dGF0ZShjZWxsX0lEID0gcGFzdGUwKGNlbGxfSUQsICJfIiwgaSkpICU+JQogICAgZHBseXI6OnNlbGVjdChjKCJjZWxsX0lEIiwgImJyb2FkIikpCn0KCm1ldGEgPC0gZG8uY2FsbChyYmluZCwgbWV0YSkKCiMgbWVhbiBhdmVyYWdlIGV4cHJlc3Npb24gYnkgc3RhZ2UKbWVhbl9BRSA8LSBhdmcubW9kLmVpZ2VuZ2VuZXNbLDE6MjJdICU+JQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJjZWxsX0lEIikgJT4lCiAgZHBseXI6Om11dGF0ZShzdGFnZSA9IHN0cmluZ3I6OnN0cl9zdWIoY2VsbF9JRCwgLTEpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHN0YWdlID0gZmFjdG9yKHN0YWdlLCBsZXZlbHMgPSBjKDE6MyksIGxhYmVscyA9IGMoIkQwNSIsICJEMDciLCAiRDEwIikpKSAlPiUKICBkcGx5cjo6bGVmdF9qb2luKG1ldGEsIGJ5ID0gImNlbGxfSUQiKSAlPiUKICB0aWJibGU6OmNvbHVtbl90b19yb3duYW1lcygiY2VsbF9JRCIpICU+JQogIHRpZHlyOjp1bml0ZSgic3RhZ2VfY2wiLCBzdGFnZSwgYnJvYWQsIHNlcCA9ICJfIikgJT4lCiAgZHBseXI6Omdyb3VwX2J5KHN0YWdlX2NsKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlX2VhY2gobWVhbikgJT4lCiAgZHBseXI6OnVuZ3JvdXAoKSAlPiUKICBnYXRoZXIoa2V5PSJtb2R1bGUiLCB2YWx1ZSA9ICJBRSIsIC1zdGFnZV9jbCkgJT4lCiAgZHBseXI6OmxlZnRfam9pbihtb2RfYW5ub3RbLCBjKDEsMyldLCBieSA9ICJtb2R1bGUiKSAlPiUKICB0aWR5cjo6c2VwYXJhdGUoInN0YWdlX2NsIiwgYygic3RhZ2UiLCAiYnJvYWQiKSwgc2VwID0gIl8iLCByZW1vdmUgPSBGQUxTRSkgCgpsYWJlbHNfZG90cGxvdCA8LSBzdHJpbmdyOjpzdHJfcmVtb3ZlKG1vZHVsZXNfaW5fb3JkZXIsICJeQUUiKQpuYW1lcyhsYWJlbHNfZG90cGxvdCkgPC0gbW9kdWxlc19pbl9vcmRlcgoKcGxvdF9jbHVzdGVycyA8LSBjKCJwcm9nZW5pdG9ycyIsICJuZXVyb25zIiwgIlJQIiwgIkZQIiwgInBlcmljeXRlcyIsICJPUEMiLCAiTUZPTCIsICJtaWNyb2dsaWEiLCAiYmxvb2QiKQoKbWVhbl9tb2QgPC0gZ2dwbG90KGRhdGEgPSBtZWFuX0FFLAogIGFlcygKICAgIHggPSBzdGFnZSwKICAgIHkgPSBBRSwKICAgIGNvbG9yID0gZmFjdG9yKGJyb2FkLCBsZXZlbHMgPSBwbG90X2NsdXN0ZXJzKSwKICAgIGdyb3VwID0gYnJvYWQsCiAgICBsYWJlbCA9IGFubm90YXRpb24KICAgICkKICApICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY2x1c3RfY29sJGNvbG9yW21hdGNoKHBsb3RfY2x1c3RlcnMsIGNsdXN0X2NvbCRicm9hZCldKSArCiAgdGhlbWVfYncoKSArCiAgIyBmYWNldCB3cmFwIHdpdGggcmVvcmRlcmVkIGZhY3RvcnMKICBmYWNldF93cmFwKHZhcnMoZmFjdG9yKG1vZHVsZSwgbGV2ZWxzID0gdW5pcXVlKG1lYW5fQUUkbW9kdWxlKVttb2R1bGVfb3JkZXJdKSksCiAgICAgICAgICAgICBzY2FsZXMgPSAiZnJlZV95IiwKICAgICAgICAgICAgIG5yb3cgPSA0LAogICAgICAgICAgICAgbmNvbCA9IDYpICsKICBsYWJzKGNvbG9yID0gImJyb2FkIikgKwogIGdndGl0bGUoIkF2ZXJhZ2UgbW9kdWxlIGV4cHJlc3Npb24gYnkgc3RhZ2UiKQoKcGxvdGx5OjpnZ3Bsb3RseShtZWFuX21vZCkKCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18yX21lYW5fbW9kX0FFLnBkZiIsIGhlaWdodCA9IDcsIHdpZHRoID0gMTIpCiNQbG90IHNwbGl0IHRzbmUKbWVhbl9tb2QKYGBgCgojIFZsblBsb3RzIG9mIGF2Zy4gbW9kdWxlIGV4cC4gYnkgc3RhZ2UgYW5kIHNldXJhdCBjbHVzdGVyIAoKIyMgY29sb3JlZCBieSBtb2R1bGUKCmBgYHtyIGF2ZXJhZ2UtbW9kdWxlLWV4cHJlc3Npb24tcGVyLWNsdXN0ZXIsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0yMH0KCiMgcmVvcmRlciBzZXVyYXQgY2x1c3RlcnMKZm9yIChpIGluIHNlcShteS5zZXMpKSB7CiAgbXkuc2VzW1tpXV0kc2V1cmF0X2NsdXN0ZXJzIDwtIGZhY3RvcigKICBteS5zZXNbW2ldXSRzZXVyYXRfY2x1c3RlcnMsCiAgbGV2ZWxzID0gbGV2ZWxzKG15LnNlc1tbaV1dJHNldXJhdF9jbHVzdGVycylbYXMuaW50ZWdlcihzdHJfZXh0cmFjdChvcmRfbGV2ZWxzW1tpXV0sICJcXGR7MSwyfSQiKSldCiAgKQoKfQoKdnBsb3RzIDwtIGxpc3QoKQoKZm9yIChpIGluIHNlcShteS5zZXMpKSB7CiAgCiAgbW9kcyA8LSBjb2xuYW1lcyhteS5zZXNbW2ldXVtbXV0pW2dyZXAoIl5BRSIsY29sbmFtZXMobXkuc2VzW1tpXV1bW11dKSldCiAgCiAgdnBsb3RzW1tpXV0gPC0gVmxuUGxvdCgKICAgICAgICBteS5zZXNbW2ldXSwKICAgICAgICBmZWF0dXJlcyA9IG1vZHNbbW9kdWxlX29yZGVyXSwKICAgICAgICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLAogICAgICAgIHN0YWNrID0gVFJVRSwgZmxpcCA9IFRSVUUsCiAgICAgICAgY29scyA9IHN1YnN0cmluZyhtb2RzLCAzKVttb2R1bGVfb3JkZXJdKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGx0eSA9ICJkYXNoZWQiKQogIAp9Cgp2cGxvdHNbWzFdXQp2cGxvdHNbWzJdXQp2cGxvdHNbWzNdXQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzJfQUVfYnlfY2x1c3Rlcl9tb2Rjb2wucGRmIiwgaGVpZ2h0ID0gMjAsIHdpZHRoID0gMTApCnZwbG90c1tbMV1dCnZwbG90c1tbMl1dCnZwbG90c1tbM11dCmBgYAoKIyMgY29sb3JlZCBieSBjZWxsIHR5cGUKCmBgYHtyIHZsbi1ieS1jbHVzdGVyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MjB9CmNsdXN0X2NvbCA8LSByZWFkLmNzdigifi9zcGluYWxfY29yZF9wYXBlci9hbm5vdGF0aW9ucy9icm9hZF9jbHVzdGVyX21hcmtlcl9jb2xvcnMuY3N2IikKCnZwbG90c19pZCA8LSBsaXN0KCkKCmZvciAoaSBpbiBzZXEobXkuc2VzKSkgewogICAgCiAgbW9kcyA8LSBjb2xuYW1lcyhteS5zZXNbW2ldXVtbXV0pW2dyZXAoIl5BRSIsY29sbmFtZXMobXkuc2VzW1tpXV1bW11dKSldCiAgCiAgdnBsb3RzX2lkW1tpXV0gPC0gVmxuUGxvdCgKICAgIG15LnNlc1tbaV1dLAogICAgZmVhdHVyZXMgPSBtb2RzW21vZHVsZV9vcmRlcl0sCiAgICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLAogICAgc3RhY2sgPSBUUlVFLCBmbGlwID0gVFJVRSwKICAgIGZpbGwuYnkgPSAiaWRlbnQiLAogICAgY29scyA9IGNvbF90YWJsZVtbaV1dJGNvbG9yW2FzLmludGVnZXIoc3RyX2V4dHJhY3Qob3JkX2xldmVsc1tbaV1dLCAiXFxkezEsMn0kIikpXSkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsdHkgPSAiZGFzaGVkIikKfQoKdnBsb3RzX2lkW1sxXV0KdnBsb3RzX2lkW1syXV0KdnBsb3RzX2lkW1szXV0KCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18yX0FFX2J5X2NsdXN0ZXJfY2x1Y29sLnBkZiIsIGhlaWdodCA9IDIwLCB3aWR0aCA9IDEwKQp2cGxvdHNfaWRbWzFdXQp2cGxvdHNfaWRbWzJdXQp2cGxvdHNfaWRbWzNdXQpgYGAKCiMgTU4gbW9kdWxlcwoKRm9yIHRoZSBmaWd1cmVzIHdlIHNwZWNpZmljYWxseSBzZWxlY3QgdGhlIHR3byBNTiBtb2R1bGVzIGFuZCBwbG90IHRoZW0gYXMgVmxuIHBsb3RzLgoKYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD01fQp0bXAgPC0gYXZnLm1vZC5laWdlbmdlbmVzIAogIAppZGVudGljYWwocm93bmFtZXModG1wKSwgY29sbmFtZXMobXkuc2VjKSkKIyBhZGQgbWV0YSBkYXRhIHRvIHRoZSBpbnQgc2V1cmF0IG9iamVjdApteS5zZWMgPC0gQWRkTWV0YURhdGEobXkuc2VjLCB0bXApCm15LnNlYyA8LSBBZGRNZXRhRGF0YShteS5zZWMsIG15Lm1ldGFtW2MoImZpbmUiLCAic2FtcGxlX2NlbGx0eXBlIildKQoKY3VzdG9tX29yZGVyIDwtIGMocGFzdGUobmFtZXMob3JkX2xldmVscylbMV0sIG9yZF9sZXZlbHNbWzFdXSwgc2VwID0gJ18nKSwKICAgICAgICAgICAgICAgICAgcGFzdGUobmFtZXMob3JkX2xldmVscylbMl0sIG9yZF9sZXZlbHNbWzJdXSwgc2VwID0gJ18nKSwKICAgICAgICAgICAgICAgICAgcGFzdGUobmFtZXMob3JkX2xldmVscylbM10sIG9yZF9sZXZlbHNbWzNdXSwgc2VwID0gJ18nKSkKCm15LnNlYyRzYW1wbGVfY2VsbHR5cGUgPC0gZmFjdG9yKAogIG15LnNlYyRzYW1wbGVfY2VsbHR5cGUsCiAgbGV2ZWxzID0gY3VzdG9tX29yZGVyCiAgKQoKdmxuX2luZCA8LSBWbG5QbG90KG15LnNlYywKICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gYygiQUVkYXJrcmVkIiwgIkFFbGlnaHRncmVlbiIsICJBRWRhcmtncmVlbiIpLAogICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAic2FtcGxlX2NlbGx0eXBlIiwKICAgICAgICAgICAgICAgICAgIHN0YWNrID0gVFJVRSwKICAgICAgICAgICAgICAgICAgIGZsaXAgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgY29scyA9IGMoImRhcmtyZWQiLCAibGlnaHRncmVlbiIsICJkYXJrZ3JlZW4iKSxwdC5zaXplID0gMQogICAgICAgICAgICAgICAgICApICsKICAgIE5vTGVnZW5kKCkKCnZsbl9pbmQKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzJfQUVfc2VsZWN0ZWRfbW9kLnBkZiIsIGhlaWdodCA9IDIyLCB3aWR0aCA9IDIwKQp2bG5faW5kCmBgYAoKYGBge3J9ClZsblBsb3QoCiAgICBteS5zZWMsCiAgICBmZWF0dXJlcyA9IG1vZHNbbW9kdWxlX29yZGVyXSwKICAgIGdyb3VwLmJ5ID0gInNhbXBsZV9jZWxsdHlwZSIsCiAgICBzdGFjayA9IFRSVUUsIGZsaXAgPSBUUlVFLAogICAgY29scyA9IHN1YnN0cmluZyhtb2RzLCAzKVttb2R1bGVfb3JkZXJdKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGx0eSA9ICJkYXNoZWQiKQoKCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9GaWdfMl9BRV9ieV9jbHVzdGVyX2ludGVncmF0ZWRfZGF0YS5wZGYiLCBoZWlnaHQgPSAyMCwgd2lkdGggPSAzMCkKVmxuUGxvdCgKICAgIG15LnNlYywKICAgIGZlYXR1cmVzID0gbW9kc1ttb2R1bGVfb3JkZXJdLAogICAgZ3JvdXAuYnkgPSAic2FtcGxlX2NlbGx0eXBlIiwKICAgIHN0YWNrID0gVFJVRSwgZmxpcCA9IFRSVUUsCiAgICBjb2xzID0gc3Vic3RyaW5nKG1vZHMsIDMpW21vZHVsZV9vcmRlcl0pICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbHR5ID0gImRhc2hlZCIpCgoKYGBgCgpgYGB7cn0KIyBEYXRlIGFuZCB0aW1lIG9mIFJlbmRlcmluZwpTeXMudGltZSgpCgpzZXNzaW9uSW5mbygpCmBgYAoK